package com.wmd.core.swagger;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.wmd.core.annotation.ApiJsonEntity;
import com.wmd.core.annotation.ApiJsonObject;
import com.wmd.core.annotation.ApiJsonObjects;
import com.wmd.core.annotation.ApiJsonProperty;
import com.wmd.core.operation.UnionCriteria;
import com.wmd.core.util.HumpTransform;
import com.wmd.core.util.StringUtil;
import io.swagger.annotations.ApiModelProperty;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.LineNumberAttribute;
import javassist.bytecode.annotation.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import springfox.documentation.spi.service.contexts.OperationContext;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Simple to Introduction
 *
 * @projectName:sms
 * @packageName:lyc.example.sms.swagger
 * @className:CustomOperationModelsProviderPlugin
 * @Description:[一句话描述该类的功能]
 * @Author:luyincheng
 * @CreateDate:2021/1/19 13:59
 * @blog:[方法具体上行/下行参数文档]
 */
@Component
@Order // plugin加载顺序，默认是最后加载
@Profile({"dev","test"})
public class CustomBuilderPlugin implements OperationBuilderPlugin {

    @Autowired
    private TypeResolver typeResolver;

    private final static String basePackage = "com.wmd.core.entity.extra."; // 动态生成的虚拟DTO Class的包路径
    public static boolean HUMP_TO_LINE=true;

    @Override
    public void apply(OperationContext context) {
        Optional<ApiJsonObject> optional = context.findAnnotation(ApiJsonObject.class);
        Optional<ApiJsonObjects> optionals = context.findAnnotation(ApiJsonObjects.class);

        List<ApiJsonObject> apiJsonObjects = new ArrayList<>();
        if (optional.isPresent()) {
            apiJsonObjects.add(optional.get());
        }
        if (optionals.isPresent()) {
            for (ApiJsonObject a : optionals.get().value()) {
                if (apiJsonObjects.size() >= 2) {//忽略多余注释
                    break;
                }
                apiJsonObjects.add(a);
            }
        }
        //System.out.println("a numbers:"+optionals.get().value().length);
        if (apiJsonObjects.size() > 0) {
            Set<Integer> ParamterOrResponseSet=new HashSet<>();
            for (ApiJsonObject apiJsonObject : apiJsonObjects) {
                if(ParamterOrResponseSet.contains(2)){//2为已经设置参数和返回值，后边都不处理
                    break;
                }
                if(ParamterOrResponseSet.contains(apiJsonObject.ParamterOrResponse())){
                    continue;
                }
                ParamterOrResponseSet.add(apiJsonObject.ParamterOrResponse());

                String name = null;
                name = apiJsonObject.name() + "_" + context.getName()+"_"+apiJsonObject.ParamterOrResponse(); // model 名称
                // 根据参数上的ApiJsonObject注解中的参数动态生成Class
                ApiJsonProperty[] properties =apiJsonObject.properties();
                ApiJsonEntity[] entities = apiJsonObject.entities();


                Class c = createRefModel(properties, entities, name,apiJsonObject.description());

                ResolvedType type = typeResolver.resolve(c);
                DocumentationContext documentationContext = context.getDocumentationContext();
                Set<ResolvedType> resolvedTypes = documentationContext.getAdditionalModels();
                resolvedTypes.add(type); // 向documentContext的Models中添加我们新生成的Class

                ModelRef modelRef=new ModelRef(name);
                if(apiJsonObject.ParentGeneric()!=Object.class){
                    modelRef=new ModelRef(apiJsonObject.ParentGeneric().getSimpleName()+"«"+name+"»");
                   /* apiJsonObject.ParentGeneric().getGenericSuperclass();
                    Class Pc=createRefParentModel(apiJsonObject.ParentGeneric(),apiJsonObject.ParentField(),name,"泛型封装");
                    if(Pc!=null){
                        ResolvedType typeP = typeResolver.resolve(Pc);
                        resolvedTypes.add(typeP);
                        modelRef=new ModelRef(apiJsonObject.ParentGeneric().getSimpleName()+"<<"+name+">>");
                    }*/

                }

                if(apiJsonObject.ParamterOrResponse()==1||apiJsonObject.ParamterOrResponse()==2){//返回类型或者全部
                    System.out.println("添加返回说明："+name);

                    HashSet<ResponseMessage> responseMessages = new HashSet<>();
                    responseMessages.add(new ResponseMessage(200,"返回json用例说明",modelRef,null,null));
                    context.operationBuilder().responseMessages(responseMessages);
//                        operationContext.operationModelsBuilder().addReturn(type).build();
                }
//            context.operationModelsBuilder().addReturn(rt).build();
            }

        }

    }
    private Class createRefParentModel(Class<?> PClass,String ParentField,String CName,String description) {
        Field f= null;
        try {
            f = PClass.getDeclaredField(ParentField);
        } catch (NoSuchFieldException e) {
            return null;
        }
        String PClassName=PClass.getSimpleName()+"<<"+CName+">>";
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = pool.getOrNull(basePackage + PClassName);
        if (ctClass != null) {
            // 这行代码很重要 如果没有的话 那就不能和devTool 一起使用
            // 销毁旧的类
            ctClass.detach();
        }
        ctClass = pool.makeClass(basePackage + PClassName);

        try {
            List<String> excludeFields=new ArrayList();
            excludeFields.add(ParentField);
            createAndAddField(PClass, ctClass,"",excludeFields);
            ClassFile classFile = ctClass.getClassFile();
            ConstPool constpool = classFile.getConstPool();

            //添加泛型属性field
            CtField ctField = new CtField(ClassPool.getDefault().get(basePackage+CName), ParentField, ctClass);
            ctField.setModifiers(Modifier.PUBLIC);
            AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
            Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constpool);
            ann.addMemberValue("value", new StringMemberValue("结果集", constpool));
            attr.addAnnotation(ann);
            ctField.getFieldInfo().addAttribute(attr);
            ctClass.addField(ctField);//添加属性


            //添加类注解
            AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
            Annotation annotation = new Annotation("io.swagger.annotations.ApiModel", constpool);
            annotation.addMemberValue("value", new StringMemberValue(PClassName, constpool));
            annotation.addMemberValue("description", new StringMemberValue(description, constpool));
            annotationsAttribute.setAnnotation(annotation);
            ctClass.getClassFile().addAttribute(annotationsAttribute);

//            byte[] byteArray = ctClass.toBytecode();
//            FileOutputStream output = new FileOutputStream("E:/"+name+".class");
//            output.write(byteArray);
//            output.close();
//            System.out.println("文件写入成功!!!");

            return ctClass.toClass();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }
    /**
     * 根据propertys中的值动态生成含有Swagger注解的javaBeen
     */
    private Class createRefModel(ApiJsonProperty[] properties, ApiJsonEntity[] entities, String name,String description) {
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = pool.getOrNull(basePackage + name);
        if (ctClass != null) {
            // 这行代码很重要 如果没有的话 那就不能和devTool 一起使用
            // 销毁旧的类
            ctClass.detach();
        }
        ctClass = pool.makeClass(basePackage + name);

        try {
            for (ApiJsonProperty property : properties) {
                ctClass.addField(createField(property, ctClass));
            }
            for (ApiJsonEntity entity : entities) {
                String Field_Prefix;
                if(entity.ContainPrefix()){
                    Field_Prefix=entity.value().getSimpleName()+"_";
                }else {
                    Field_Prefix="";
                }

                createAndAddField(entity.value(), ctClass,Field_Prefix,null);
            }

            ClassFile classFile = ctClass.getClassFile();
            ConstPool constpool = classFile.getConstPool();

            AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
            Annotation annotation = new Annotation("io.swagger.annotations.ApiModel", constpool);
            annotation.addMemberValue("value", new StringMemberValue(name, constpool));
            annotation.addMemberValue("description", new StringMemberValue(description, constpool));

            annotationsAttribute.setAnnotation(annotation);

            classFile.addAttribute(annotationsAttribute);

//            byte[] byteArray = ctClass.toBytecode();
//            FileOutputStream output = new FileOutputStream("E:/"+name+".class");
//            output.write(byteArray);
//            output.close();
//            System.out.println("文件写入成功!!!");

            return ctClass.toClass();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    private void createAndAddField(Class<?> entity, CtClass ctClass,String Field_Prefix,List<String> excludeFields) {

//        String Field_Prefix=entity.getSimpleName();

        for (Field field : entity.getDeclaredFields()) {
            if(excludeFields!=null&&excludeFields.contains(field.getName())){
                continue;
            }
            if(UnionCriteria.serialVersionUID.equals(field.getName())){
                continue;
            }
            try {
                String fieldName= HUMP_TO_LINE?HumpTransform.lineToHump(Field_Prefix+field.getName()):Field_Prefix+field.getName();
                try {
                    //重复属性不添加
                    if (ctClass.getField(fieldName) != null) {
                        continue;
                    }
                } catch (NotFoundException e) {

                }

                CtField ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()),fieldName, ctClass);
                ctField.setModifiers(Modifier.PUBLIC);

                ConstPool constPool = ctClass.getClassFile().getConstPool();

                String description = "";
                String example = "";
                boolean required = false;
                if (field.isAnnotationPresent(ApiModelProperty.class)) {
                    ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                    description = apiModelProperty.value();
                    example = apiModelProperty.example();
                    required = apiModelProperty.required();
                }

                AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
                Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
                ann.addMemberValue("value", new StringMemberValue(description, constPool));
//                ann.addMemberValue("example", new StringMemberValue(example, constPool));
                ann.addMemberValue("required", new BooleanMemberValue(required, constPool));

                attr.addAnnotation(ann);
                ctField.getFieldInfo().addAttribute(attr);

                ctClass.addField(ctField);//添加属性

            } catch (CannotCompileException | NotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据property的值生成含有swagger apiModelProperty注解的属性
     */
    private CtField createField(ApiJsonProperty property, CtClass ctClass)
            throws NotFoundException, CannotCompileException {
        ConstPool constPool = ctClass.getClassFile().getConstPool();
        CtField ctField = new CtField(ClassPool.getDefault().get(property.type().getName()), property.key(), ctClass);
        ctField.setModifiers(Modifier.PUBLIC);
        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
        ann.addMemberValue("value", new StringMemberValue(property.description(), constPool));
//        ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
        ann.addMemberValue("required", new BooleanMemberValue(property.required(), constPool));


/*
        if (property.type().getSimpleName().equals("Integer")){
            ann.addMemberValue("example", new IntegerMemberValue(Integer.parseInt(property.example()), constPool));
        }
        else if (property.type().getSimpleName().equals("Double")){
            ann.addMemberValue("example", new DoubleMemberValue(Double.parseDouble(property.example()), constPool));
        }
        else (property.type().getSimpleName().equals("String")){
            ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
        }
*/

        attr.addAnnotation(ann);
        ctField.getFieldInfo().addAttribute(attr);

        return ctField;
    }

    /**
     * @Title: MapApiReader.java
     * @Description: 参数类型，后续可以增加多少类型
     * @date
     */
    private CtClass getFieldType(Class<?> type) throws NotFoundException {
        CtClass fileType = null;
        switch (type.getSimpleName()) {
            case "String":
                fileType = ClassPool.getDefault().get(String.class.getName());
                break;
            case "Integer":
                fileType = ClassPool.getDefault().get(Integer.class.getName());
            case "Double":
                fileType = ClassPool.getDefault().get(Double.class.getName());
                break;
        }
        return fileType;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }


}
